#!/usr/bin/env php
<?php

require "Console/Getopt.php";
//error_reporting(0);

$con = new Console_Getopt;
$args = $con->readPHPArgv();
array_shift($args);
$shortoptions = 'f:';
$retval = $con->getopt( $args, $shortoptions);
if(is_object($retval)) {
	usage();
}
foreach ($retval[0] as $kv_array) {
    $opt[$kv_array[0]] = $kv_array[1];
}
if(!$opt['f']) {
	usage();
}
if(!file_exists($opt['f'])) {
	print "Trace file ${opt['f']} does not exist\n";
	exit;
}
$IN = fopen($opt['f'], "r");
if(!$IN) {
	print "Trace file ${opt['f']} could not be opened\n";
	exit;
}

$path_parts = pathinfo($opt['f']);
$outfile = "cachegrind.out.".$path_parts['basename'];
$OUT = fopen($outfile, "w");
if(!$OUT) {
	print "Destination file $outfile could not be opened.\n";
	exit;
}

while(($line = fgets($IN)) !== false) {
	$line = rtrim($line);
	if($line == "END_HEADER") {
		break;
	}
}
$tree = array();
$callstack = array();
while(($line = fgets($IN)) !== false) {
	$line = rtrim($line);
	$args = explode(" ", $line);
	if($args[0] == '!') {
		$file_lookup[$args[1]] = $args[2];
	}
	else if($args[0] == '&') {
		$function_lookup[$args[1]] = $args[2];
		$function_type[$args[1]] = ($args[3] == 2)?"USER":"INTERNAL";
	}
	else if($args[0] == '+') {
		$val = array(function_id => $args[1], 
		             file_id => $args[2],
					 line => $args[3], 
					 cost => 0);
		array_push($callstack, $val);
	}
	else if($args[0] == '-') {
		// retrieve $called to discard
		$called = array_pop($callstack);
		// retrieve $caller for reference
		$caller = array_pop($callstack);
		$called_id = $called['function_id'];
		
		// Set meta data if not already set'
		if(!array_key_exists($called_id, $tree)) {
			$tree[$called_id] = $called;
			// initialize these to 0
			$tree[$called_id]['cost_per_line'] = array();
		}
		if($caller !== null) {
			$caller['child_calls']++;
			$caller_id = $caller['function_id'];
			if(!array_key_exists($caller_id, $tree)) {
				$tree[$caller_id] = $caller;
			}
			$caller['cost'] += $called['cost'];
			$tree[$caller_id]['called_funcs'][$tree[$caller_id]['call_counter']++][$called_id][$called['file_id']][$called['line']] += $called['cost'];
			array_push($callstack, $caller);
		}
		if(is_array($called['cost_per_line'])) {
			foreach($called[cost_per_line] as $file => $lines) {
				foreach($lines as $line => $cost) {
					$tree[$called_id]['cost_per_line'][$file][$line] += $cost;
				}
			}
		}
	}
	else if($args[0] == '@') {
		$called = array_pop($callstack);
		switch(count($args)) {
			case 6:
				$file = $args[1];
				$line = $args[2];
				$real_tm = $args[5];
				break;
			case 4:
				$file = $called['file_id'];
				$line = $called['line'];
				$real_tm = $args[3];
				break;
				
		}
		$called['cost_per_line'][$file][$line] += $real_tm;
		$called['cost'] += $real_tm;
		$total_cost += $real_tm;
		array_push($callstack, $called);
	}
}

ob_start();
print "events: Tick\n";
print "summary: $total_cost\n";
printf("cmd: %s\n", $file_lookup[1]);
print "\n";

foreach($tree as $caller => $data) {
	$filename = $file_lookup[$data['file_id']]?$file_lookup[$data['file_id']]:"???";
	printf("ob=%s\n", $function_type[$caller]);
	printf("fl=%s\n", $filename);
	printf("fn=%s\n", $function_lookup[$caller]);
	if(is_array($data['cost_per_line'])) {
		foreach($data['cost_per_line'] as $file => $lines) {
			foreach($lines as $line => $cost) {
				print "$line $cost\n";
			}
		}
	}
	else if ($data['cost']) {
		printf("COST %s %s\n", $items['line'], $items['cost']);
	}
	else {
		print_r($items);
	}
	if(is_array($data['called_funcs'])) {
		foreach($data['called_funcs'] as $counter => $items) {
			foreach($items as $called_id => $costs) {
				if(is_array($costs)) {
					printf("cob=%s\n", $function_type[$called_id]);
					printf("cfn=%s\n", $function_lookup[$called_id]);
					foreach($costs as $file => $lines) {
						printf("cfi=%s\ncalls=1\n", $file_lookup[$file]);
						foreach($lines as $line => $cost) {
							print "$line $cost\n";
						}
					}
				}
			}
		}
	}
	print "\n";
}
$buffer = ob_get_clean();
print "Writing kcachegrind compatible output to $outfile\n";
fwrite($OUT, $buffer);

function usage()
{
	print <<<EOD
pprof2calltree -f <tracefile>

EOD;
	exit(1);


}
?>
